package com.seventeen.monster.redisson.core;

import com.seventeen.monster.common.utils.spring.SpringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * @author seventeen
 */
public class DistributeLockUtils {
    private final static Logger log = LoggerFactory.getLogger(DistributeLockUtils.class);

    private static RedissonClient redissonClient;

    private static RedissonClient getRedissonClient() {
        if (redissonClient == null) {
            redissonClient = SpringUtils.getBean(RedissonClient.class);
        }
        return redissonClient;
    }

    /**
     * 执行需要加锁保护且无参数无返回值的操作，并自动管理锁的获取与释放。
     * 使用 lock() 方法，该方法会一直阻塞直到获取到锁。
     * *不推荐*
     */
    public static void lockAndExecute(String lockKey, Runnable operation) {
        RLock lock = getRedissonClient().getLock(lockKey);
        // 获取锁，会一直阻塞直到成功获取锁
        lock.lock();
        try {
            // 执行操作
            operation.run();
        } finally {
            // 确保锁被释放
            lock.unlock();
        }
    }

    /**
     * 执行需要加锁保护的操作，并自动管理锁的获取与释放。
     */
    public static <T> void tryLockAndExecute(String lockKey, long waitTime, long leaseTime, TimeUnit unit, Runnable operation) {
        RLock lock = getRedissonClient().getLock(lockKey);
        try {
            // 尝试获取锁
            boolean isLockAcquired = lock.tryLock(waitTime, leaseTime, unit);
            if (!isLockAcquired) {
                log.error("Could not acquire lock {}", lockKey);
                throw new RuntimeException("Could not acquire lock " + lockKey);
            }
            // 执行操作
            operation.run();
        } catch (InterruptedException e) {
            log.error("acquire lock {} exception,", lockKey, e);
            throw new RuntimeException("Could not acquire lock " + lockKey, e);
        } finally {
            // 确保锁被释放
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 执行需要加锁保护且无参数无返回值的操作，并自动管理锁的获取与释放。
     * 使用 lock(leaseTime, unit) 方法，该方法会在指定的时间后自动释放锁。
     */
    public static void lockAndExecute(String lockKey, long leaseTime, TimeUnit unit, Runnable operation) {
        RLock lock = getRedissonClient().getLock(lockKey);
        try {
            // 获取锁，如果锁不可用会一直等待直到成功获取锁
            // 设置锁的自动释放时间
            lock.lock(leaseTime, unit);
            // 执行操作
            operation.run();
        } finally {
            // 确保锁被释放
            lock.unlock();
        }
    }
}
